
#include "ka-system/context.h"
#include "ka-system/event.h"

using namespace ka;

void Event::clear() {
    action = 0;
}

float Event::getFloatExtra(int argNum) const {
    float f = 0.0f;
    if (argNum<_extraCount) {
        if (argNum<(sizeof(_defExtra)/sizeof(_Arg))) {
            f = _defExtra[argNum].asFloat();
        }
    }
    return f;
}

float Event::_Arg::asFloat() const {
    float v = 0.0f;
    switch (type) {
        case EXTRA_FLOAT:
            v = u.f;
            break;
        case EXTRA_UINT:
            v = (float)u.ui;
            break;
    }
    return v;
}

IEventHook::~IEventHook() {
}

IEventReceiver::~IEventReceiver() {
}

IEventPoster::~IEventPoster() {
}

EventHandler::EventHandler(const UUID &eventUid) 
: _eventUid(eventUid),
_context(NULL),
_hook(NULL),
_next(NULL),
_prev(NULL) {
    // register the handler
    Context *cxt = Context::getCurrent();
    if (cxt) {
        _context = cxt;
        int eventChannel = cxt->getEventChannel(eventUid);
        EventHandler *handlerList = cxt->getEventHandlers(eventChannel);
        _next = handlerList;
        if (handlerList) {
            handlerList->_prev = this;
        }
        cxt->setEventHandlers(this);
    }
}

EventHandler::~EventHandler() {
    removeFromContext();
}

void EventHandler::handleEvent(const Event &e) {
    if (_hook) {
        _hook->onEvent(e);
    }
}

void EventHandler::removeFromContext() {
    if (_context!=NULL) {
        int eventSlot = _context->getEventChannel(_eventUid);
        if (_context->getEventHandlers(eventSlot) == this) {
            _context->setEventHandlers(_next);
        }
        if (_next) {
            _next->_prev = _prev;
        }
        if (_prev) {
            _prev->_next = _next;
        }
        _context = NULL;
    }
}

void EventHandler::setEventHook(IEventHook *hook) {
    _hook = hook;
}

EventHandler *EventHandler::getNext() const {
    return _next;
}

const UUID &EventHandler::getEventUid() const {
    return _eventUid;
}

EventRaiser::EventRaiser(const UUID &eventUid) 
: _eventUid(eventUid),
_cachedEventSlot(-1) {
}

void EventRaiser::raiseEvent(const Event &e) {
    Context *cxt = Context::getCurrent();
    if (cxt != NULL) {
        EventHandler *handlers = cxt->getEventHandlers(_cachedEventSlot);
        if (handlers == NULL || !(handlers->getEventUid()==_eventUid)) {
            _cachedEventSlot = cxt->getEventChannel(_eventUid);
            handlers = cxt->getEventHandlers(_cachedEventSlot);
        }
        while (handlers) {
            handlers->handleEvent(e);
            handlers = handlers->getNext();
        }
    }
}

EventQueue::EventQueue(uint32_t limit)
: _events(NULL),
_limit(0),
_write(0),
_read(0),
_count(0) {
    if (limit > 0) {
        _events = new Event[limit];
        _limit = limit;
    }
}

EventQueue::~EventQueue() {
    delete[] _events;
}

bool EventQueue::postEvent(const Event &in) {
    bool r = false;
    
    if (_lock.lock(UINT64_MAX)) {
        if (_count < _limit) {
            _events[_write] = in;
            _write = (_write + 1) % _limit;
            _count++;
            r = true;
        }
        _lock.unlock();
    }
    return r;
}

bool EventQueue::receiveEvent(Event &out, uint64_t timeoutMillis) {
    bool r = false;
    
    if (_lock.lock(timeoutMillis)) {
        if (_count > 0) {
            out = _events[_read];
            _read = (_read + 1) % _limit;
            _count--;
            r = true;
        }
        _lock.unlock();
    }
    
    return r;
}
